"use strict"
let mysql1 = require("mysql");
let mysql2 = require("mysql2");
let mysql = mysql1;
const redis = require('redis');
const EventEmitter = require('events');
const { Client } = require('ssh2');
class RedisUtils extends EventEmitter {
    constructor(config) {
        super();
        this.conneced = false;
        this.reConnected = false;
        this.classType = `RedisUtils`;
        this.sshTunnel = new Client();
        this.config = config;
        this.reTryDelay = config?.reTryDelay || 5000;
        this.pong = undefined;
        this.reConnect = config?.reConnect || true;
        this.init();
    }
    async init() {
        this.conneced = false;
        let options = {
            // host: this.config.ser, 
            // port: this.config?.port || 6379,
            url: `redis://${this.config?.ser || 'localhost'}:${this.config?.port || 6379}`,
            password: this.config?.passwd || '',
        }
        if (this.config?.ssh) {
            await new Promise((resolve, reject) => {
                this.sshTunnel.connect(this.config.ssh).on('ready', () => {
                    this.log('SSH Tunnel', 'connected', 'info');
                    this.sshTunnel.forwardOut('127.0.0.1', 0, 'localhost', options.port, async (err, stream) => {
                        if (err) {
                            this.emit('error', err);
                            this.log('SSH Tunnel', err, 'error');
                        } else {
                            this.redisClient = await redis.createClient({ stream });
                        }
                        resolve()
                    });
                });
            })
        } else {
            this.redisClient = await redis.createClient(options)
        }
        if (this.redisClient) {
            this.redisClient.connect();
            this.redisClient.on("end", err => {
                this.conneced = false;
                clearInterval(this.pong);
                this.pong = undefined;
                this.reConnected = this.reConnect
                if (this.reConnect === true) {
                    setTimeout(() => {
                        this.init();
                    }, 2000);
                } else {
                    this.emit('close', err);
                }
            }).on("error", err => {
                this.conneced = false;
                clearInterval(this.pong);
                this.pong = undefined;
                this.emit('error', err);
                setTimeout(() => {
                    this.init();
                }, this.reTryDelay);
            }).on("connect", err => {
                this.conneced = true;
                if (this.pong !== undefined) clearInterval(this.pong);
                this.pong = setInterval(() => {
                    this.redisClient.ping();
                }, 10 * 1000);
                this.emit('connect');
            });
        }
    }
    log(obj) {
        if (this.config.log) {
            this.config.log(arguments[0], arguments[1], arguments[2]);
        } else {
            console.log(arguments[1])
        }
    }
    async remove(key) {
        try {
            result = await this.redisClient.del(key)
        } catch (error) {
        }
    }
    async get(key, _default = '', onlyString = false) {
        let result = _default
        try {
            result = await this.redisClient.get(key)
            if (result == null || result == undefined) result = _default
            if (onlyString == false) {
                try {
                    if (result.startsWith('{') && result.endsWith('}')) {
                        result = JSON.parse(result)
                    }
                } catch (error) {
                }
            } else {
                if (result instanceof Object) result = JSON.stringify(result)
                if (result instanceof Array) result = JSON.stringify(result)
            }
        } catch (error) { }
        return result
    }
    async set(key, value, expir = -1) {
        if (value instanceof Object) value = JSON.stringify(value)
        if (value instanceof Array) value = JSON.stringify(value)
        try {
            await this.redisClient.set(key, value, 'EX', expir)
        } catch (error) { }
    }
}
class MySQLUtils extends EventEmitter {
    constructor(config) {
        super()
        this.classType = `MySqlUtils`
        this.config = config
        let options = {
            connectionLimit: 30,
            host: this.config.host,
            port: this.config.port || 3306,
            user: this.config.user,
            password: this.config.password,
            database: this.config.database,
            //设置超时时间解决握手不活动超时问题
            connectionLimit: 10240,
            connectTimeout: 60 * 60 * 1000,
            acquireTimeout: 60 * 60 * 1000,
            timeout: 60 * 60 * 1000,
        }
        if (this.config?.mode === `mysql2`) {
            delete options['acquireTimeout'];
            delete options['timeout'];
            mysql = mysql2;
        }
        this.pool = mysql.createPool(options)
        this.statusCode = {
            emptySql: -10,
            queryError: 500,
            queryOk: 200,
        }
    }
    /**
     * 异步调用Query方法
     */
    async syncQuery(sql) {
        return (new Promise((resolve, reject) => {
            this.query(sql, result => {
                resolve(result)
            })
        }))
    }
    query(sql, callback) {
        let that = this
        if (!sql) {
            if (typeof callback === `function`) {
                callback({
                    code: this.statusCode.emptySql,
                    status: 'err',
                    result: `sql is empty`,
                });
            }
            return;
        }
        this.pool.query(sql, function (err, rows, fields) {
            if (err) {
                if (typeof callback === `function`) {
                    callback({
                        code: that.statusCode.queryError,
                        status: 'err',
                        result: err,
                    });
                }
            } else {
                if (typeof callback === `function`) {
                    callback({
                        code: that.statusCode.queryOk,
                        status: 'ok',
                        result: JSON.parse(JSON.stringify(rows)),
                        // result: {
                        //     fields,
                        //     rows,
                        // },
                    });
                }
            }
        });
    }
}
module.exports = { MySQLUtils, RedisUtils }